/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 2 -*- */
/**
* @file dbus.cpp
* 
* Library for easy comunication aplications through Dbus.
*
* @author Miguel Ángel Vílchez López 2008 <sacra@openmatica.es> 
*
* Codigo is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* 
* Codigo is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
* 
* You should have received a copy of the GNU General Public License along
* with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "dbus.hpp"
#include "debug.cpp"

CMDbus::CMDbus(const char* name, const char* signal, unsigned int buffer=1024,bool debug=false,const char* filename=""):debug(debug,filename)
{
	this->name=(char*)malloc((strlen(name)+1)*sizeof(char));
	this->signal=(char*)malloc((strlen(signal)+1)*sizeof(char));
	strcpy(this->name,name);
	strcpy(this->signal,signal);
	this->buffer_size = buffer;
	if ( this->init() < 0)
	{
		this->debug.error(strerror(errno),__FILE__,__FUNCTION__,(__LINE__));
		this->debug.debug("Trying to initialize the class dbus...");
	}
}

CMDbus::~CMDbus()
{
	free(this->name);
	free(this->signal);
}

int CMDbus::init()
{
	int ret=0;
	
	// initialise the errors
	dbus_error_init(&this->err);

	// connect to the bus and check for errors
	this->conn = dbus_bus_get(DBUS_BUS_SESSION, &this->err);
	if (dbus_error_is_set(&this->err))
	{
		this->debug.error(this->err.message,__FILE__,__FUNCTION__,(__LINE__));
		this->debug.debug("Trying to connect to dbus...");
		dbus_error_free(&this->err); 
	}
	if (this->conn == NULL) {
		return -1;
	}
   
	// request our name on the bus and check for errors
	char* connection = (char*)malloc(100*sizeof(char));
	sprintf(connection,"%s.mokardu.sink",this->name);
	ret = dbus_bus_request_name(this->conn, connection, DBUS_NAME_FLAG_REPLACE_EXISTING , &this->err);
	if (dbus_error_is_set(&this->err))
	{
		this->debug.error(this->err.message,__FILE__,__FUNCTION__,(__LINE__));
		this->debug.debug("Trying request our name on the bus...");
		dbus_error_free(&this->err); 
	}

	free(connection);
	
	return 0;
}

int CMDbus::ruleSignal()
{
	// add a rule for which messages we want to see
	char* rule = (char*)malloc(100*sizeof(char));
	sprintf(rule,"type='signal',interface='%s.mokardu.Type'",this->name);
	dbus_bus_add_match(this->conn, rule, &this->err); // see signals from the given interface
	dbus_connection_flush(this->conn);
	if (dbus_error_is_set(&this->err))
	{
		this->debug.error(this->err.message,__FILE__,__FUNCTION__,(__LINE__));
		this->debug.debug("Trying to set a rule on the bus...");
		free(rule);
		return -1;
	}
	
	free(rule);
	
	return 0;
}

int CMDbus::recvSignal(char* signal)
{
	// non blocking read of the next available message
	dbus_connection_read_write(this->conn, 0);
	this->msg = dbus_connection_pop_message(this->conn);
	
	// if we haven't read a message and we have the msg accopled
	if (this->msg == NULL)
		return 0;

	char* message = (char*)malloc(100*sizeof(char));
	sprintf(message,"%s.mokardu.Type",this->name);
	char* buffer = (char*)malloc(this->buffer_size*sizeof(char));
	
	// check if the message is a signal from the correct interface and with the correct name
	if (dbus_message_is_signal(this->msg, message, this->signal))
	{
		// read the parameters
		if (!dbus_message_iter_init(this->msg, &this->args))
		{
			this->debug.error("Message Has No Parameters",__FILE__,__FUNCTION__,(__LINE__));
			this->debug.debug("Trying to read the parameters...");
		}
		else if (DBUS_TYPE_STRING != dbus_message_iter_get_arg_type(&this->args)) 
		{
			this->debug.error("Argument is not string!",__FILE__,__FUNCTION__,(__LINE__));
			this->debug.debug("Trying to read the parameters...");
		}
		else
			dbus_message_iter_get_basic(&this->args, &buffer);

		strcpy(signal, buffer);
		
		free(message);
		dbus_message_unref(this->msg);
	
		return strlen(signal);
		
	}
	else
	{
		free(message);
		free(buffer);
		dbus_message_unref(this->msg);
		
		return 0;
	}
}

int CMDbus::sendSignal(char* signal)
{
	char* object = (char*)malloc(100*sizeof(char));
	char* interface = (char*)malloc(100*sizeof(char));
	sprintf(object,"/%s/mokardu/Object",name);
	sprintf(interface,"%s.mokardu.Type",name);
	dbus_uint32_t serial = 0;
	
	// create a signal & check for errors 
	this->msg = dbus_message_new_signal(object, // object name of the signal
                                 interface, // interface name of the signal
                                 this->signal); // name of the signal

	this->debug.debug("Trying to set a new message bus...");
	if (this->msg == NULL) 
	{
		this->debug.error("Message Null",__FILE__,__FUNCTION__,(__LINE__));
   	free(object);
   	free(interface);
    return -1;
	}
 	free(object);
 	free(interface);

	this->debug.debug("Trying to append arguments onto signal...");
	dbus_message_iter_init_append(this->msg, &this->args);
	if (!dbus_message_iter_append_basic(&this->args, DBUS_TYPE_STRING, &signal))
	{
		this->debug.error("Out of Memory!",__FILE__,__FUNCTION__,(__LINE__));
    return -1;
  }

	this->debug.debug("Trying to send the message and flush the connection...");
	if (!dbus_connection_send(this->conn, this->msg, &serial))
	{
		this->debug.error("Out of Memory!",__FILE__,__FUNCTION__,(__LINE__));
 		return -1;
  }
	dbus_connection_flush(this->conn);

	this->debug.debug("Signal sended...");
  
	// free the message 
	dbus_message_unref(this->msg);

	return 0;
}
